module scene.login.login;

import std.algorithm, std.stdio, std.datetime;

import scene.scene, mirdefine, mirshare, mirgame, mirsound;
import gui.msgbox, gui.logindlg;

import common.imagearray;
import share.container, share.circularbuffer, share.edcode, share.nettcp, share.protocol;

import allegro5.allegro;

public class GameLoginScene : Scene
{
    private LoginWindow LoginDlg;
    private MessageBox MsgBox;
	
    private CircularBuffer!char _c_buffer;
    private char _buffer[8192];
    private dstring m_szErrorMsg;
    //private int nBuff = 0;

    private ImageArray _iarray;
    private ImageArray _iarray1c;

	private int CurFrame, MaxFrame;
	private TickDuration StartTime; //ÇÑ ÇÁ·¡ÀÓ´ç ½Ã°£
	private bool NowOpening;
	private bool BoOpenFirst;
	private UserEntryInfo NewIdRetryUE;
	private UserEntryAddInfo NewIdRetryAdd;
	private string[] NAHelps;
	
	public bool BoUpdateAccountMode;
	public string LoginId, LoginPasswd;


	
	
	
	public this(Game game)
    {
		super(game, SceneType.Login);

        _c_buffer = CircularBuffer!char(256);

        _iarray = new ImageArray;
        _iarray.FileName = "./Data/GameInter.wil";
        _iarray.LibType = LibraryType.UseCache;
        _iarray.IVersion = InternationalVersion.English;

        _iarray1c = new ImageArray;
        _iarray1c.FileName = "./Data/Interface1c.wil";
        _iarray1c.LibType = LibraryType.UseCache;
        _iarray1c.IVersion = InternationalVersion.English;
		
        LoginDlg = new LoginWindow;
        MsgBox = new MessageBox;
    }
	
/*
	private bool CheckUserEntrys()
	{
	}
	
	private bool NewIdCheckNewId()
	{
	}
	
	private bool  NewIdCheckSSno()
	{
	}
	
	private bool NewIdCheckBirthDay()
	{
	}
*/

    public override void Initialize()
    {
        MySocket.setHandler(&OnMessageReceive);

        _iarray.initialize();
        _iarray1c.initialize();

        MsgBox.createWindow(_iarray);
        MsgBox.initialize();

        LoginDlg.createLoginWindow(_iarray1c);
    }
    
    public override void Finalize()
    {
        _iarray.finalize();
        _iarray1c.finalize();
    }
	
    public override void OpenScene()
	{
		CurFrame = 0;
		MaxFrame = 19;
		LoginId = "";
		LoginPasswd = "";

        LoginDlg.show();

		BoOpenFirst = true;

		//DLogIn.visible = true;
		//DNewAccount.visible = false;
		NowOpening = false;
        SoundMan.PlayBGMEx(bmg_intro);

        // Connect to olymp server for now..
        MySocket.ConnectToServer("141.45.120.196", 7000);
        //if ( MySocket.ConnectToServer("127.0.0.1", 7000) == false )
        //    MySocket.DisconnectToServer();
	}
	
    public override void CloseScene()
	{
        MsgBox.hide();
        LoginDlg.hide();

		SoundMan.StopBGM();

        MySocket.DisconnectToServer();
	}


	
    public override void PlayScene()
	{
        const int _LEFT_LOGIN = 0;
        const int _TOP_LOGIN = 372;
        const int _RIGHT_LOGIN = 640;
        const int _BOTTOM_LOGIN = 480;

        const int _LEFT_ID_PASS = 170;
        const int _TOP_ID_PASS = 65;

        const ALLEGRO_COLOR clear_color = al_map_rgba_f(0.0, 0.0, 0.0, 0.0);

        al_clear_to_color(clear_color);

		ALLEGRO_BITMAP* bmp;

		//if not ServerConnected then exit;
		if (BoOpenFirst)
		{
			BoOpenFirst = false;
			//DLogIn.reset;
		}

        bmp = _iarray1c[0];
		if (bmp !is null)
            al_draw_bitmap(bmp, 0, 0, 0);
            //al_draw_scaled_bitmap(bmp, 0, 0, al_get_bitmap_width(bmp), al_get_bitmap_height(bmp), 0f, 0f, SCREENWIDTH, SCREENHEIGHT, 0);

        /*bmp = ImageMan[_IMAGE_INTEREX][1];
        if (bmp !is null)
            al_draw_bitmap(bmp, _LEFT_LOGIN, _TOP_LOGIN, 0);

        bmp = ImageMan[_IMAGE_INTEREX][2];
        if (bmp !is null)
            al_draw_bitmap(bmp, _LEFT_ID_PASS, _TOP_LOGIN + _TOP_ID_PASS, 0);*/
	
		if (NowOpening)
		{
			TickDuration delay = Clock.currSystemTick() - StartTime;
			if (delay.msecs > 120)
			{
				StartTime = Clock.currSystemTick();
				CurFrame++;
			}
			if (CurFrame >= MaxFrame - 1)
			{
				CurFrame = MaxFrame - 1;
				// TODO: DoFadeIn and DoFadeOut not yet defined..
				/*if (!DoFadeOut && !DoFadeIn)
				{
					DoFadeOut = true;
					DoFadeIn = true;
					//FadeIndex = 29;
					FadeIndex = 25;
				}*/
			}
            bmp = _iarray[CurFrame];
			if (bmp !is null)
				al_draw_bitmap(bmp, 0, 0, 0);
//				al_draw_bitmap(bmp, 152, 96, 0);

			// TODO: DoFadeIn and DoFadeOut not yet defined..
			/*if (DoFadeOut)
			{
				if (FadeIndex <= 1)
				{
					WProgUse.ClearCache;
					WChrSel.ClearCache;
					Sleep(500);
					DScreen.ChangeScene(stSelectChr); //¼­¹ö¿¡¼­ Ä³¸¯ÅÍ Á¤º¸°¡ ¿À¸é ¼±ÅÃÃ¢À¸·Î ³Ñ¾î°£´Ù.
				}
			}*/
		}
	}

    public override void UpdateScene()
    {
        //ProcessRecvPacket();
    }

    private void OnMessageReceive(PTcpPacket packet)
    {
        _c_buffer.PutData(cast(char[])packet.data(), packet.size());
        destroy(packet);

        ProcessRecvPacket();
    }

    private void ProcessRecvPacket()
    {
        /+int len = _c_buffer.GetValidCount();
        if (len == 0 || len < 0) return false;
        if (len > _buffer.sizeof) return false;
        _c_buffer.GetData(_buffer[], len);

        int increase = 0;
        int first, end;

        if (_buffer[0] == '*')
        {
            first = 1;
            MySocket.sendStar();
        }
        else
            first = 0;

        while (true)
        {
            end = countUntil(_buffer[first .. len], '!');
            if (end == -1)
                break;
            
            auto slice = _buffer[first + 1 .. (first + 1) + (end - 1)];
            //OnSocketMessageRecieve(slice);
            
            end++;
            increase += end;
            
            if (_buffer[end] == '*')
            {
                MySocket.sendStar();
                end++;
            }
            
            if (_buffer[end] == '#')
                first = end;
            else
            {
                first = 0;
                break;
            }
        }

        if (increase > 0)
            _c_buffer.HeadIncrease(increase);+/
        
        int len = 0;
        char[] data = null;
        
        while (PullOutCore(data, len))
        {
            if (data !is null)
            {
                Parsing(len, data); //      ½ÇÁ¦ ÆÄ½Ì ÇÔ¼ö...
                
                //delete data;
                data = null;
            }
        }
    }
    
    private bool PullOutCore(ref char[] data, ref int length)
    {
        const char PACKET_START = '#';
        const char PACKET_END = '!';
        bool foundCore = false;
        
        length = 0;
        
        int len = _c_buffer.GetValidCount();
        
        if (len == 0 || len < 0) return false;
        if (len > _buffer.sizeof) return false;
        
        _c_buffer.GetData(_buffer[], len);
        
        
        /*int end = countUntil(_buffer[0 .. len], '!');
        if (end >= 0)
            foundCore = true;

        data = _buffer[0 .. end];
        length = end;*/
        
        int start = 0, end = 0;
        
        if (_buffer[0] == PACKET_START)
        {
            start = 0;
            for (int i = 0; i < len && !foundCore; i++)
            {
                if (_buffer[i] == PACKET_END)
                {
                    end = i;
                    length = ((end - 1) - (start + 1)) + 1;
                    if ( length < 0 ) break;
                    if ( length > len ) break;

                    // Assign a slice of _buffer to data (don't want to allocate memory)
                    data = _buffer[start + 1 .. (start + 1) + length];
                    //data[length] = 0;
                    foundCore = true;
                    break;
                }
            }
        }
        
        if (foundCore) _c_buffer.HeadIncrease(2 + length);
        
        return foundCore;
    }

    private bool setErrorMessage(int err, ref dstring message)
    {
        static immutable dstring[] error_list =
        [
             "Wrong Password.",
             "Wrong Password 3 times in a row.\\You will not be able to connect for a while.",
             "This account is actually in use or locked by abnormal terminaion.\\Please try again later.",
             "This account has no access rights.\\please change account,\\or apply for paid registration.",
             "ID does not exist or unknown error."
        ];

        if (err < 0 || err >= error_list.length)
            return false;

        message = error_list[err];
        return true;
    }

    private bool Parsing(int length, char[] data)
    {
        std.stdio.writeln("Parsing length: ", length, " data: ", data);

        int nMsgNum;
        DefaultMessage msg;

        DecodeMessage(msg, data);
        
        switch (msg.Ident)
        {
            // ID Not Found
            /+case SM_ID_NOTFOUND:
            {
                /*m_Progress  = PRG_LOGIN;
                SetErrorMessage(23,m_szErrorMsg);
                m_xMsgBox.ShowMessageBox(m_szErrorMsg, 1, _WNDIMGINDX_MSGBOX1);*/
                break;
            }+/
                // Incorrect Password
            case SM_PASSWD_FAIL:
            {
                switch (msg.Recog)
                {
                    case -1, -2, -3, -4: nMsgNum = (-msg.Recog)-1; break;
                    default: nMsgNum = 4; break;
                }

                //LoginScene.PassWdFail;
                if (setErrorMessage(nMsgNum, m_szErrorMsg))
                {
                    MsgBox.show(m_szErrorMsg, MsgBoxBtnType.Ok);
                }
                /*m_Progress  = PRG_LOGIN;
                
                if(msg.nRecog==(-2))
                    nMsgNum = ERROR_STR_LOGIN_3;
                else
                    nMsgNum = ERROR_STR_LOGIN_2;
                SetErrorMessage(nMsgNum,m_szErrorMsg);
                m_xMsgBox.ShowMessageBox(m_szErrorMsg, 1, _WNDIMGINDX_MSGBOX1);*/
                break;
            }
                
            /+case SM_CERTIFICATION_FAIL:
            {
                /*SetErrorMessage(FATAL_ERROR_STR,m_szErrorMsg);
                m_xMsgBox.ShowMessageBox(m_szErrorMsg, 1, _WNDIMGINDX_MSGBOX1);*/         // ¿ë±â
                break;
            }+/
                
            case SM_PASSOK_SELECTSERVER:
            {
                SceneMan.ChangeScene(SceneType.SelectServer);
                //m_xSelectSrv.OpenWnd();
                //m_Progress = PRG_SERVER_SELE;
                //Clear(RGB(0,0,0));
                
                break;
            }
                
            case SM_NEWID_SUCCESS:  
            {
                /*m_Progress = PRG_LOGIN;
                m_UserState = LGM_INPUT_ID;
                ZeroMemory(g_xChatEditBox.m_szInputMsg,sizeof(g_xChatEditBox.m_szInputMsg));
                SetFocusAfter();
                
                SetErrorMessage(NEW_ACCOUNT_WELL_DONE,m_szErrorMsg);
                m_xMsgBox.ShowMessageBox(m_szErrorMsg, 1, _WNDIMGINDX_MSGBOX1);*/
                break;
            }
                // New ID Create Fail
            case SM_NEWID_FAIL:
            {
                /*m_Progress  = PRG_NEW_ACCOUNT;
                
                switch(msg.nRecog)
                {
                    case 1:
                        nMsgNum=ERROR_STR_NEW_ACCOUNT_1;
                        break;
                    case 2:
                        nMsgNum=ERROR_STR_NEW_ACCOUNT_6;
                        break;
                    case 3:
                        nMsgNum=ERROR_STR_NEW_ACCOUNT_7;
                        break;
                }
                SetErrorMessage(nMsgNum,m_szErrorMsg);
                m_xMsgBox.ShowMessageBox(m_szErrorMsg, 1, _WNDIMGINDX_MSGBOX1);*/
                break;
            }
                // Change Password Success
            case SM_CHGPASSWD_SUCCESS:
            {
                //m_Progress  = PRG_LOGIN;
                break;
            }
                // Change Passsword Fail
            case SM_CHGPASSWD_FAIL:
            {
                //m_Progress  = PRG_CHANGE_PASS;  
                break;  
            }
                
            case SM_SELECTSERVER_OK:
            {
                /*char szDecodeMsg[256];
                char *pszIP = szDecodeMsg, *pszPort, *pszIdenty;
                
                ZeroMemory(szDecodeMsg, sizeof(szDecodeMsg));
                
                fnDecode6BitBuf((pszMsg + DEFBLOCKSIZE), szDecodeMsg, sizeof(szDecodeMsg));
                
                if (pszPort = strchr(szDecodeMsg, '/')){
                    *pszPort = '\0';
                    pszPort++;
                    
                    if (pszIdenty = strchr(pszPort, '/')){
                        *pszIdenty = '\0';
                        pszIdenty++;
                        
                        strcpy(g_szServerIP, pszIP);
                        g_nServerPort = atoi(pszPort);
                        
                        m_nAnimationCounter = 0;
                        
                        g_nCertifyCode = msg.nRecog;
                        g_xClientSocket.DisconnectToServer();
                        
                        Clear(RGB(0,0,0));
                        m_Progress  = PRG_TO_SELECT_CHR;
                    }
                }*/
                break;
            }
                // Connection Lost(close Connection)
            case SM_OUTOFCONNECTION:
            {
                /*m_Progress  = PRG_QUIT;                                 // Quit for Some Reason     ÇÁ·Î±×·¥ Á¾·á
                Senmsgessage(g_xMainWnd.GetSafehWnd(), WM_DESTROY, NULL, NULL);*/
                break;
            }

            default: break;
        }
        /*  
    switch(msg.wIdent)
    {
        case    SM_NEEDUPDATE_ACCOUNT:  // Need to account update
            m_Progress  = PRG_NEED_ACCOUNT_CHANGE;  // Need Account change
            break;
        case    SM_UPDATEID_SUCCESS:    // account update success
            m_Progress  = PRG_LOGIN;        // Login
            break;
        case    SM_UPDATEID_FAIL:       // account update Fail
            m_Progress  = PRG_NEED_ACCOUNT_CHANGE;  // Login
            break;
    }
*/

        return true;
    }

}